/**
 * Copyright (C), 2017-2018, XXX有限公司
 * FileName: LogUtil
 * Author:   zengjian
 * Date:     2018/7/31 10:41
 * Description:
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package fun.codedesign.yinxue.util;

import java.io.*;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 〈日志记录工具类〉<br>
 * 〈INFO:打印到控制台，ERROR:打印到控制台并且记录文件日志〉
 *
 * @author zengjian
 * @create 2018/7/31 10:41
 */
public class LogUtil {

    private static final String LINE = System.getProperty("line.separator");
    static File logPath = new File("E:/log/error.txt");

    // 设置全局输出等级 范围依次是: INFO > ERROR
    final static int INFO = 1;
    final static int ERROR = 2;
    // 默认为0，均不输出，可以设置输出INFO以上或者ERROR以上日志，打包时调整为2
    static int globalLevel = 2;

    // 不同的日志记录方式
    final static int CONSOLE = 1 << 0; // 打印到控制台
    final static int FILE = 1 << 1; // 记录到文件
    final static int DATABASE = 1 << 2; // 记录到数据库

    // 可设置不
    static int debugOutLevel = CONSOLE;
    static int infoOutLevel = CONSOLE;
    static int errorOutLevel = CONSOLE + FILE;

    static boolean limitMessageLength = false;

    public static void info(Object src, String format, Object... message) {
        if (globalLevel < INFO) {
            return;
        }
        StringBuilder builder = buildLog("INFO:", src, MessageFormat.format(format, message));
        if ((infoOutLevel & CONSOLE) != 0) {
            console0(builder);
        }
        if ((infoOutLevel & FILE) != 0) {
            file0(builder);
        }
    }

    public static void error(Object src, String format, Object... message) {
        if (globalLevel < ERROR) {
            return;
        }
        StringBuilder builder = buildLog("ERROR:", src, MessageFormat.format(format, message));
        if ((errorOutLevel & CONSOLE) != 0) {
            console0(builder);
        }
        if ((errorOutLevel & FILE) != 0) {
            file0(builder);
        }
    }

    private static StringBuilder buildLog(String header, Object src, Object message) {
        StringBuilder builder = new StringBuilder(128);
        builder.append(header);
        builder.append(getTimestamp());
        builder.append(":");
        builder.append(getClassName(src));
        builder.append(":");
        builder.append(message);
        builder.append(LINE);
        return builder;
    }

    private static StringBuilder buildLog(String header, Object src, Object message, Exception e) {
        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter = new PrintWriter(stringWriter);
        e.printStackTrace(printWriter);
        return buildLog(header, src, message).append(stringWriter.getBuffer()).append(LINE);
    }

    public static void info(Object src, Object message) {
        if (globalLevel < INFO) {
            return;
        }
        StringBuilder builder = buildLog("INFO:", src, message);
        if ((infoOutLevel & CONSOLE) != 0) {
            console0(builder);
        }
        if ((infoOutLevel & FILE) != 0) {
            file0(builder);
        }
    }

    private static void console0(StringBuilder builder) {
        PrintStream printStream = System.out;
        String message = builder.toString();
        // 限制控制台打印信息每条在256个字符内
        if (limitMessageLength){
            if (message.length() > 256) {
                message = message.substring(0, 256) +"..." + LINE;
            }
        }
        printStream.print(message); // 信息自带换行，所以不用printLn
    }

    @SuppressWarnings("all")
    private static String getClassName(Object src) {
        if (src instanceof Class) {
            return ((Class) src).getName();
        }
        return src.getClass().getName();
    }

    public static void error(Object src, String format, Exception e, Object... message) {
        if (globalLevel < ERROR) {
            return;
        }
        StringBuilder builder = buildLog("ERROR:", src, MessageFormat.format(format, message), e);
        if ((errorOutLevel & CONSOLE) != 0) {
            console0(builder);
        }
        if ((errorOutLevel & FILE) != 0) {
            file0(builder);
        }
    }

    public static void error(Object src, String message, Exception e) {
        if (globalLevel < ERROR) {
            return;
        }
        StringBuilder builder = buildLog("ERROR:", src, message, e);
        if ((errorOutLevel & CONSOLE) != 0) {
            console0(builder);
        }
        if ((errorOutLevel & FILE) != 0) {
            file0(builder);
        }
    }

    private static void file0(StringBuilder builder) {
        if (!logPath.exists()) {
            logPath.getParentFile().mkdirs();
            try {
                logPath.createNewFile();
            } catch (IOException e) {
                error(LogUtil.class, "初始化日志记录文件失败", e);
                return;
            }
            FileWriter fileWriter = null;
            try {
                fileWriter = new FileWriter(logPath, true);
                fileWriter.write(builder.toString() + LINE);
            } catch (IOException e) {
                error(LogUtil.class, "记录日志文件失败", e);
            } finally {
                CloseUtil.close(fileWriter);
            }
        }
    }

    private static String getTimestamp() {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss");
        return format.format(new Date());
    }
}